package com.openatc.agent.controller;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.openatc.agent.gateway.WebSocketServer;
import com.openatc.agent.model.*;
import com.openatc.agent.service.AscsDao;
import com.openatc.agent.service.FaultDao;
import com.openatc.agent.utils.RedisTemplateUtil;
import com.openatc.comm.data.MessageData;
import com.openatc.comm.model.UdpCommunicationStaticPort;
import com.openatc.core.model.RESTRet;
import com.openatc.core.model.RESTRetBase;
import com.openatc.core.util.DateUtil;
import com.openatc.core.util.RESTRetUtils;
import com.openatc.model.model.AscsBaseModel;
import com.openatc.model.model.StatusPattern;
import com.openatc.model.model.SystemEvent;
import com.openatc.model.model.SystemEventDetails;
import com.sun.management.OperatingSystemMXBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import static com.openatc.agent.handler.AgentHandler.*;
import static com.openatc.agent.model.DeviceStatus.OFFLINE;
import static com.openatc.agent.model.DeviceStatus.ONLINE;
import static com.openatc.core.util.DateUtil.compareCreateTime;
import static com.openatc.model.model.SystemEvent.EVENT_LEVEL_URGENT;
import static com.openatc.model.model.SystemEvent.TYPE_SYSTEM;
import static com.openatc.model.model.SystemEventDetails.MSG_TYPE_OFFLINE;
import static com.openatc.model.model.SystemEventDetails.MSG_TYPE_ONLINE;

/**
 * @author laoxia
 * @Date 2021/10/11 9:30
 * @Describetion 获取服务器系统信息
 */
@Slf4j
@RestController
public class ServerController {
    private static Logger logger = Logger.getLogger(ServerController.class.toString());

//    @Value("${datebasename}")
    private String dbname;
    @Autowired
    RedisTemplateUtil redisTemplate;
    @Autowired
    private QYDeviceStatusManager qyDeviceStatusManager;
    @Autowired
    AscsDao ascDao;

    @Autowired
    FaultDao faultDao;

    @Autowired
    public JdbcTemplate jdbcTemplate;

    @Value("${agent.version}")
    private String serviceVersion;
    // 打包时间
    @Value("${agent.build.time}")
    private String serviceBuildDate;

    @Value("${agent.lang.type:cn}")
    private String agentLangType;

    @Value("${historydata.save.time}")
    private Integer historyDataSaveTime;
    @Autowired
    private WebSocketServer webSocketServer;

    private Gson gson = new Gson();

    // 保存方案消息的时间计数器，每5分钟保存方案信息
    private int savePatternIndex = 0;

    private String addrstr;
    private String hostname;
    private static final Map<String,Integer> deviceOfflineCount = new ConcurrentHashMap<>();

    @PostConstruct
    public void init(){
        InetAddress addr = null;
        try {
            addr = InetAddress.getLocalHost();
            addrstr = addr.getHostAddress();
            hostname = addr.getHostName();
        } catch (UnknownHostException e) {
            addrstr = "Unknown";
            hostname = "Unknown";
        }
        // 只查询一次数据库名称
        this.dbname = ascDao.getDBName();
    }

    /**
     * 新建一个设备的记录
     * @param agentId
     */
    private static void newOfflineCount(String agentId) {
        deviceOfflineCount.put(agentId,1);
    }


    /**
     * 记录设备离线的次数
     * @param agentId
     */
    private static void recordOfflineCount(String agentId) {
        Integer count = deviceOfflineCount.get(agentId);
        deviceOfflineCount.put(agentId,count + 1);
    }

    /**
     * 移除设备的记录次数
     * @param agentId
     */
    private static void removeOfflineCount(String agentId) {
        deviceOfflineCount.remove(agentId);
    }

    /**
     * 获取当前设备离线的记录次数
     * @param agentId
     * @return
     */
    private Integer getOfflineCount(String agentId) {
        return deviceOfflineCount.get(agentId);
    }
    /**
     * @return RESTRetBase
     * @Title: getTemplate
     * @Description: 根据设备id获取路口类型和相位
     */
    @GetMapping(value = "system/status")
    public RESTRetBase getServerStatus() {

        JsonObject jsonStatus = new JsonObject();
        jsonStatus.addProperty("version", serviceVersion);

        return RESTRetUtils.successObj(jsonStatus);
    }

    /**
     * @param
     * @return
     * @descripation 从Redis中，获取服务状态
     **/
    @GetMapping(value = "/system/status/server/all")
    public RESTRet<Set<ServerState>> GetServerState() {

        Set<String> keyset =  redisTemplate.getKeys("server:status:*");
        Set<ServerState> ValueSet = new HashSet();
        for(String key : keyset){
            // 过滤掉不是当前host的OpenATC-admin服务
            if (key.contains("openatc-admin")) {
                String temKey = "server:status:openatc-admin-" + hostname;
                if (!temKey.equals(key)) continue;
            }
            String value = redisTemplate.getValue(key);
            if(value == null)
                continue;

            ServerState serverState = gson.fromJson(value,ServerState.class);
            ValueSet.add(serverState);
        }
        return RESTRetUtils.successObj(ValueSet);
    }

    // 更新服务信息
    @Scheduled(cron = "0 0/4 * * * ?")
    public void UpdateServerStatus() {

        logger.warning("UpdateServerStatus Schedule Start!");

        try {
            // 服务信息
            UpdateMainServerStaus();

            // 数据库信息
            UpdateDBStaus();

            // Redis信息
            UpdateRedisStaus();

            // 清理通讯对象的缓存信息
            ClearCommCache();

            // 清理离线的设备信息缓存
            AscsDao.clearDevsCacheFromDown();
        } catch (Exception e) {
            logger.warning("UpdateServerStatus error! " + e.getMessage());
        }

        logger.warning("UpdateServerStatus Schedule Finish!");

    }

    /**
     * 更新倒计时，1秒
     */
    @Scheduled(fixedRate = 1000)
    private void updatePatternCountDown(){
        try{
            for(MessageData md : ascDao.getStatusPatternMap().values() ){

                JsonObject mdo = md.getData().getAsJsonObject();
                if(mdo.get("current_stage") == null)
                    return;

                int control = mdo.get("control").getAsInt();
                int patternid = 0;
                if( mdo.get("patternid") != null ) patternid = mdo.get("patternid").getAsInt();
                int current_stage = mdo.get("current_stage").getAsInt();

//                int curTime = mdo.get("curTime").getAsInt();

                CountDownPattern cdPattern = ascDao.cdPatternMap.get(md.getAgentid());
                if( cdPattern == null ) {
                    // 初始化
                    cdPattern = new CountDownPattern();
                    ascDao.cdPatternMap.put(md.getAgentid(), cdPattern);
                }else{
                    // 方案发生变化，清空阶段信息
                    if( patternid !=  cdPattern.getPatternid()){
                        cdPattern.getStages().clear();
                        cdPattern.setControl(control);
                        cdPattern.setPatternid(patternid);
                    }
                }
                //
                CountDownStage cdStage = cdPattern.getStages().get(current_stage);
                if( cdStage == null ){
                    cdStage = new CountDownStage();
                    cdStage.setStageNum( current_stage );
                    cdPattern.getStages().put(current_stage,cdStage );
                }

                int lastSecStageNum = cdPattern.getCurStageNum();

                // 判断阶段是否切换
                if( lastSecStageNum !=  current_stage){
                    cdPattern.setCurStageNum( current_stage );
                    cdStage.setLaststageNum(lastSecStageNum);
                    cdStage.setCurDuration(0);
//                    int split = cdStage.getSplit();
                    // 更新上个阶段的绿信比
                    CountDownStage lastcdStage = cdPattern.getStages().get(lastSecStageNum);
                    lastcdStage.setNeststageNum( current_stage );
                    lastcdStage.setSplit( lastcdStage.getCurDuration() );
                }

                // 定时控制计算倒计时
                // 持续时间加1
                int curDuration = cdStage.getCurDuration()+1;
                cdStage.setCurDuration( curDuration );

                if( control == 5 || control == 10 || control == 11 || control == 13){
                    int current_stagecd = Math.max( cdStage.getSplit() - curDuration, 0 );
                    cdPattern.setCurStageCd( current_stagecd );
                    mdo.addProperty("current_stagecd",current_stagecd);
                }

//                logger.info( cdPattern.toString() );

            }
        }catch (Exception e){
            logger.warning("updatePatternCountDown Error! " + e.getMessage());
        }

    }

    /**
     * 定时把方案保存进数据库，30秒
     */
    @Scheduled(fixedRate = 30*1000)
    private void savePattern2DB(){
        logger.warning("savePattern2DB Schedule Start!");

        try {
            List<String> strs = new ArrayList<>();
            int size = patternsqlqueue.size();
            for(int i=0; i< size; i++){
                strs.add( patternsqlqueue.poll() );
            }
            ascDao.batchUpdate(strs);
        }catch (Exception e){
            logger.warning("savePattern2DB Error! " + e.getMessage());
        }
        logger.warning("savePattern2DB Schedule Finish!");

    }

    // 更新数据库表dev中mode和control字段，同时更新信号机注册消息
    @Scheduled(cron = "*/31 * * * * ?")
    public void UpdatePgsqlData(){
        logger.warning("UpdatePgsqlData Schedule Start!");

        try {
            UpdateModeAndControl();
            // 定时推送信号机状态
            reportDeviceStatus();
        }catch (Exception e){
            e.printStackTrace();
            logger.warning("UpdateModeAndControl Error! " + e.getMessage());
        }
        logger.warning("UpdatePgsqlData Schedule Finish!");

    }
    public void reportDeviceStatus() {
        if (qyDeviceStatusManager.isReportDeviceStatus()) {
            String sql = String.format("select agentid,case (LOCALTIMESTAMP - lastTime)< '1 min' when true then 'UP' else 'DOWN' END AS state from dev");
            List<AscsBaseModel> devices = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(AscsBaseModel.class));
            if (!CollectionUtils.isEmpty(devices)) {
                List<DeviceStatus> statuses = new ArrayList<>(devices.size());
                for (AscsBaseModel device : devices) {
                    DeviceStatus status = DeviceStatus.convertDeviceStatus(device);
                    qyDeviceStatusManager.pushDeviceStatus(status);
                    statuses.add(status);
                }
                logger.info("report device status to: " + qyDeviceStatusManager.getBootstrapServers() + "." + statuses);
            }
        }
    }

    // 定时清理通讯对象的缓存，避免由于异常出现的一直返回4109错误
    private void ClearCommCache() {
        long currentTime = System.currentTimeMillis()/1000;
        Map<String, UdpCommunicationStaticPort> udpCommMap = UdpCommunicationStaticPort.messageMap;
        for (String key: udpCommMap.keySet()) {
            UdpCommunicationStaticPort comm = udpCommMap.get(key);
            long timedif = currentTime - comm.creatTime;
            if(timedif > 300) // 清除超过一分钟的缓存key
                udpCommMap.remove(key);
        }
    }

    // 更新控制模式和控制方式字段值，并同时更新设备在线状态
    // 保证每次更新设备在线状态能正常完成
    // loginMap中保存了所有在线的设备，可以当作在线设备的缓存
    private void UpdateModeAndControl() {
        int fiveMinuteFlag = 10;
        Map<String,MessageData> loginMap = AscsDao.getLoginMap();

        logger.warning("Start Update Login Msg!" + " Dev Size:" + loginMap.size() + " ID: " +  loginMap.keySet() );
        // 更新离线故障计数器
        try {
            updateOfflineFaultCount(loginMap.keySet());
        } catch (Exception e) {
            e.printStackTrace();
            logger.warning("update offline count failed!e: " + e.getMessage());
        }
        // 从redis中批量获取方案状态
        Map<String, MessageData> messageDatas = null;
        try {
             messageDatas = redisTemplate.muliGetStatusPatternFromRedis(loginMap.keySet());
             // redis中获取不到数据，应该是设备上报方案异常，需检查设备上报功能
             if( messageDatas == null || messageDatas.isEmpty() ) {
                 logger.warning("Batch get statusPattern is null!");
//                 return;
             }
        } catch (Exception e) {
            if (messageDatas == null) {
                messageDatas = new HashMap<>();
            }
            logger.warning("Batch get statusPattern error!" + e.getMessage());
        }
        // 批量更新数据库表的sql
        List<String> sqls = new ArrayList<>();
        Set<String> onLineDevs = new HashSet<>();

        for (MessageData mdLogin : loginMap.values()) {
            String agentid = mdLogin.getAgentid();

            // 5分钟保存一次关键周期
            if (savePatternIndex == fiveMinuteFlag) {
                String savePatternSql = getSavePatternSql(messageDatas.get(agentid));
                if (StringUtils.hasLength(savePatternSql)) {
                    sqls.add(savePatternSql);
                }
            }

            try {

                int second = DateUtil.getBetweenDatesFromNow(mdLogin.getSts());
                // 在线的设备更新数据库
                if (second <= 35) {
                    String thirdplatformid = mdLogin.getThirdpartyid();
                    String model = mdLogin.getModel();

                    StatusPattern mdStatusPattern = null;
                    // 如果从redis中批量获取方案状态失败，此处会报空指针
                    try {
                        mdStatusPattern = gson.fromJson(messageDatas.get(agentid).getData().getAsJsonObject(), StatusPattern.class);
                    } catch (Exception e) {
                        logger.warning("Get Redis Pattern error! agentid: " + agentid + " statusPattern: " + messageDatas.get(agentid));
                    }

                    Integer mode = null;
                    Integer control = null;
                    Integer patternid = null;
                    if (mdStatusPattern != null) {
                        mode = mdStatusPattern.getMode();
                        control = mdStatusPattern.getControl();
                        patternid = mdStatusPattern.getPatternid();
                    }
                    String sql = String.format("update dev set mode=%s,control=%s,lastTime=LOCALTIMESTAMP,model='%s',patternid=%s,thirdplatformid='%s' where agentid='%s'",
                                mode, control, model,patternid, thirdplatformid,agentid);
                    onLineDevs.add(agentid);
                    if (StringUtils.hasLength(sql)) {
                        sqls.add(sql);
                    }
                }else{
                    // 超过2分钟不上报消息，再记录离线故障，避免丢包引起频繁记录故障
                    // 第一次发现离线，新建记录次数
                    newOfflineCount(agentid);
//                    if (recordOfflineFault(agentid)) {
//                        try {
//                            logger.warning("Create Offline Fault,MessageData: " + mdLogin);
//                            createDevOfflineFault(agentid);
//                            // 发送离线通知
//                            sendSystemEvent(agentid,MSG_TYPE_OFFLINE);
//                            removeOfflineCount(agentid);
//                        } catch (Exception e) {
//                            logger.warning("Send Offline Event Failed! Exception Msg: " + e.getMessage());
//                        }
//                    }
                    // 若注册消息的时间超过，认为设备离线，不再更新数据库表中的数据, 并删除在线设备的缓存
                    AscsDao.delelteLoginMap(agentid);
                    AscsDao.deleteAscstoCache(agentid);
                }
            } catch (Exception e) {
                logger.warning("Concat update dev sql error! AgentID: " + mdLogin.getAgentid() + " E：" + e.getMessage() + "! Report login data:" + mdLogin);
            }
        }

        // 批量执行sql（批量更新设备状态）
        if (!CollectionUtils.isEmpty(sqls))
            ascDao.batchUpdate(sqls);
        // 更新设备离线故障记录中的上线时间
        if (!CollectionUtils.isEmpty(onLineDevs)) {
            updateFaultRenewTime(onLineDevs);
        }

        // 5分钟计时
        if (savePatternIndex == fiveMinuteFlag)
            savePatternIndex = 0;
        savePatternIndex++;
    }

    /**
     * 更新离线故障计数器
     * @param agentIds
     */
    private void updateOfflineFaultCount(Set<String> agentIds) {
        for (String agentId : deviceOfflineCount.keySet()) {
            if (agentIds.contains(agentId)) {
                removeOfflineCount(agentId);
            } else {
                // 更新离线记录次数
                recordOfflineCount(agentId);
                if (recordOfflineFault(agentId)) {
                    try {
                        createDevOfflineFault(agentId);
                        // 发送离线通知
                        sendSystemEvent(agentId,MSG_TYPE_OFFLINE);
                        removeOfflineCount(agentId);
                    } catch (Exception e) {
                        logger.warning("Send Offline Event Failed! Exception Msg: " + e.getMessage());
                    }
                }
            }
        }
    }

    /**
     * 两分钟没有收到设备上报的消息，记录离线故障
     * @param agentId
     * @return
     */
    private boolean recordOfflineFault(String agentId) {
        Integer offlineCount = getOfflineCount(agentId);
        return offlineCount > 3;
    }

    /**
     * 发送上线通知
     */
    private void sendSystemEvent(String agentId, int detailsMsgType) {
        AscsBaseModel ascsFromCache = ascDao.getAscsFromCache(agentId);
        String deviceName = "";
        if (ascsFromCache != null) {
            deviceName =ascsFromCache.getName();
        } else {
            AscsBaseModel ascsByID = ascDao.getAscsByID(agentId);
            deviceName = ascsByID.getName();
        }

        SystemEvent systemEvent = new SystemEvent();

        String source = "来自系统的通知";
        String content = "";
        systemEvent.setType(TYPE_SYSTEM);
        systemEvent.setContent(content);
        systemEvent.setSource(source);
        systemEvent.setLevel(EVENT_LEVEL_URGENT);
        systemEvent.setCreatetime(DateUtil.date2str(new Date()));
        SystemEventDetails details = new SystemEventDetails();
        details.setAgentid(agentId);
        details.setMsgtype(detailsMsgType);
        details.setName(deviceName);
        systemEvent.setDetails(details);
        webSocketServer.sendSystemEvent(systemEvent);
    }

    /**
     * 更新故障记录中设备的恢复时间
     * @param onLineDevs
     */
    private void updateFaultRenewTime(Set<String> onLineDevs) {
        String inCond = AscsDao.getInCond(new ArrayList<>(onLineDevs));
        String selectOfflineFault = String.format("select agentid,m_w_faultid from fault " +
                                                " where agentid in %s and m_w_fault_type=901 and m_un_fault_renew_time isnull", inCond);
        List<Map<String, Object>> unProceedFault = jdbcTemplate.queryForList(selectOfflineFault);
        if (!CollectionUtils.isEmpty(unProceedFault)) {
            Set<String> faultIds = new HashSet<>();
            Set<String> needUpdateDev = new HashSet<>();
            for (Map<String, Object> fault : unProceedFault) {
                String agentId = (String) fault.get("agentid");
                Long faultId = (Long) fault.get("m_w_faultid");
                needUpdateDev.add(agentId);
                faultIds.add(String.valueOf(faultId));
            }
            String updateFaultRenewTime = String.format("update fault set m_un_fault_renew_time=%s,enumerate='2',operator='admin' " +
                    " where m_w_faultid in %s and agentid in %s",
                    System.currentTimeMillis() / 1000,AscsDao.getInCond(new ArrayList<>(faultIds)), AscsDao.getInCond(new ArrayList<>(needUpdateDev)));
            jdbcTemplate.update(updateFaultRenewTime);
            // 发送上线通知
            for (String agentid : needUpdateDev) {
                sendSystemEvent(agentid,MSG_TYPE_ONLINE);
            }
        }
    }

    /**
     * 增加一条设备离线的故障记录
     * @param agentid
     */
    private void createDevOfflineFault(String agentid) {
        // 增加一条故障记录，记录设备离线时间
        Fault fault = new Fault();
        fault.setAgentid(agentid);
        Long faultId = generateFaultId(agentid);
        if (faultId == null) {
            logger.warning("Dev offline,but create faultId error! agentid: " + agentid);
            return;
        }
        fault.setM_wFaultID(faultId);
        fault.setM_byFaultBoardType(9);  // 其他故障
        fault.setMUnFaultOccurTime(System.currentTimeMillis() / 1000 - 30);
        fault.setM_wFaultType(901l);
        fault.setM_byFaultLevel(1);
        fault.setEnumerate("0");
        fault.setM_unFaultRenewTime(0l);
        faultDao.save(fault);
    }

    /**
     * 防止agentid_faultid不唯一，每次生成fault之前查询，如果存在则重新生成
     * 最多重复100次，如果重复100次还没有生成成功，则返回null
     * @param agentid
     * @return
     */
    private long generateFaultId(String agentid) {
        int count = 0;
        Long faultId = null;
        while (count < 100) {
            faultId = (long) new Random().nextInt(10000);
            String sql = String.format("select count(*) from fault where agentid='%s' and m_w_faultid=%s", agentid, faultId);
            int num = jdbcTemplate.queryForObject(sql, Integer.class);
            if (num == 0) {
                break;
            }
            count++;
        }
        return faultId;
    }


    /**
     * 拼接保存方案的sql
     * @param msg
     * @return
     */
    private String getSavePatternSql(MessageData msg) {
        JsonObject msgdata = null;
        int fiveminIndex;
        int fifteenminIndex;
        int sixtyminIndex;
        int weekIndex;
        int cycle;
        try {
            boolean isEffective = judgeMsg(msg);
            if (!isEffective) return null;

            msgdata = msg.getData().getAsJsonObject();

            msgdata.remove("overlap");

            JsonArray phases = msgdata.get("phase").getAsJsonArray();
            for (JsonElement phase : phases) {
                JsonObject phosedata = phase.getAsJsonObject();
                phosedata.remove("conphase");
                phosedata.remove("type");
                phosedata.remove("countdown");
                phosedata.remove("pedtype");
                phosedata.remove("pedcountdown");
                phosedata.remove("close");
            }
            fiveminIndex =  DateUtil.getTimeCount(DateUtil.dateNormalStrToTimestamp(msg.getCreatetime()),300000); // 当前时间是第几个5分钟
            fifteenminIndex = DateUtil.getTimeCount(DateUtil.dateNormalStrToTimestamp(msg.getCreatetime()),300000 * 3);; // 当前时间是第几个15分钟
            sixtyminIndex = DateUtil.getTimeCount(DateUtil.dateNormalStrToTimestamp(msg.getCreatetime()),300000 * 12);; // 当前时间是第几个60分钟
            weekIndex = DateUtil.getDayofweek();
            cycle = msgdata.get("cycle").getAsInt();

        } catch (Exception e){
            logger.warning("Deal Pattern Data error!:" + e.getMessage());
            return null;
        }

        String sql = String.format("INSERT INTO pattern (agentid,time,data,cycle,fivemintag,fifteenmintag,sixtymintag,week) VALUES " +
                        "('%s','%s','%s','%s','%s','%s','%s','%s')",
                msg.getAgentid(),msg.getCreatetime(),msgdata.toString(),cycle,fiveminIndex,fifteenminIndex,sixtyminIndex,weekIndex);
        return sql;
    }

    /**
     * 判断当前方案数据是否有效
     * @param msg
     * @return
     */
    private boolean judgeMsg(MessageData msg) {
        if (msg == null) return false;
        JsonElement data = msg.getData();
        if (data == null) return false;

        if (msg.getCreatetime() == null) return false;

        Date date = DateUtil.stringToDate(msg.getCreatetime());
        long createTime = date.getTime();
        long now = System.currentTimeMillis();
        if (now - createTime > 5000 * 24) return false;

        JsonObject msgdata = data.getAsJsonObject();
        // 没有相位数据
        JsonElement phaseElement = msgdata.get("phase");
        if (phaseElement == null) return false;

        return true;
    }
    // 更新Redis信息
    private void UpdateRedisStaus() {
        ServerState serverState = new ServerState();
        serverState.setServer("redis");
        serverState.setType("cache");
        Date day=new Date();
        SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        serverState.setUpdatetime(sdf.format(day));
        serverState.setData(redisTemplate.GetRedisInfo(agentLangType) );

        redisTemplate.setValue("server:status:redis",gson.toJson(serverState));
    }

    // 更新数据库信息
    private void UpdateDBStaus() {
        ServerState serverState = new ServerState();
        serverState.setServer("postgresql");
        serverState.setType("db");
        Date day=new Date();
        SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        serverState.setUpdatetime(sdf.format(day));

        int count = ascDao.getConnectCount();
        String size = ascDao.getDBSize(dbname);

        String str1=null;
        String str2=null;
        if(agentLangType.equals("cn")){
            str1 = "连接数";
            str2 = "数据库大小";
        }else{
            str1 = "Connect Count";
            str2 = "DB Size";
        }

        JsonObject jo = new JsonObject();
        jo.addProperty(str1,String.valueOf(count) );
        jo.addProperty(str2 ,size);
        serverState.setData(jo);

        redisTemplate.setValue("server:status:postgresql",gson.toJson(serverState));
    }

    // 更新服务系统信息
    private void UpdateMainServerStaus() {

        ServerState serverState = new ServerState();
        serverState.setServer("OpenATC-Admin");
        serverState.setType("main");
        Date day=new Date();
        SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        serverState.setUpdatetime(sdf.format(day));
        JsonObject jo = new JsonObject();

        String str1=null;
        String str2=null;
        String str3=null;
        String str4=null;
        String str5=null;
        String str6=null;
//        String str7=null;
//        String str8=null;
//        String str9=null;
        String str10=null;
        String str11=null;
        String str12=null;
        String str13=null;
        if(agentLangType.equals("cn")){
            str1="服务版本 ";
            str2="编译时间 ";
            str3="总内存 ";
            str4="可用内存 ";
            str5="进程CPU占用 ";
            str6="系统CPU占用 ";
//            str7="剩余磁盘空间 ";
//            str8="占用磁盘空间 ";
//            str9="总磁盘空间 ";
            str10="消息队列大小 ";
            str11="今日消息总数 ";
            str12="5分钟RPS ";
            str13="主机名 ";
        }else{
            str1="Version";
            str2="Build Time";
            str3="Total RAM";
            str4="Available　RAM";
            str5="Process Cpu Load";
            str6="System Cpu Load";
//            str7="Free Space Disk ";
//            str8="Used Space Disk ";
//            str9="Total Space Disk ";
            str10="Report Msg Queue ";
            str11="Today Msg Count ";
            str12="5 min RPS ";
            str13="Host Name :";
        }

        // 服务版本
        jo.addProperty(str1,serviceVersion);
        jo.addProperty(str2 ,serviceBuildDate);

        // 主机名和IP
        jo.addProperty(str13,hostname);
        jo.addProperty("IP " ,addrstr);

        // 内存信息
        OperatingSystemMXBean mem = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
        jo.addProperty(str3,mem.getTotalPhysicalMemorySize() / 1024 / 1024 / 1024 + "GB");
        jo.addProperty(str4 ,mem.getFreePhysicalMemorySize() / 1024 / 1024 / 1024 + "GB");

        // CPU信息
        OperatingSystemMXBean cpu = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
        double processCpuLoad = cpu.getProcessCpuLoad();
        double systemCpuLoad = cpu.getSystemCpuLoad();
        jo.addProperty(str5,String.format("%.2f",processCpuLoad));
        jo.addProperty(str6, String.format("%.2f",systemCpuLoad));

        // 磁盘信息
//        int diskNum=1;
//        File[] disks = File.listRoots();
//        for (File file : disks) {
//            long free = file.getUsableSpace() / 1024 / 1024 / 1024;
//            long total = file.getTotalSpace() / 1024 / 1024 / 1024;
//            jo.addProperty(str7 + diskNum,free + "G");
//            jo.addProperty(str8 + diskNum,(total - free) + "G");
//            jo.addProperty(str9 + diskNum,total + "G");
//            diskNum++;
//        }

        // 内部消息队列
        jo.addProperty(str10 ,MdQueue.size());
        jo.addProperty(str11 ,MdTodayCount);
        jo.addProperty(str12 ,MdminQps/300);
        MdminQps = 0;

        serverState.setData(jo);

        redisTemplate.setValue("server:status:openatc-admin"+"-"+hostname ,gson.toJson(serverState));
    }

    // 每天凌晨12点定时执行任务，清理历史数据并统计设备状态数据
    @Scheduled(cron = "0 0 0 * * ?")
    public void clearHistoryData() {
        try{
            logger.warning("Clearing Schedule Start!");
            // 清理历史方案数据
            String sql = String.format("delete from pattern where time < (now() - interval '%s day')",historyDataSaveTime);
            jdbcTemplate.execute(sql);
            sql = String.format("delete from pattern_realtime where time < (now() - interval '%s day')",historyDataSaveTime);
            jdbcTemplate.execute(sql);
            logger.warning("Clear Pattern Data From DB Finished!");
            // 清理完重建索引以加快查询
//            sql = String.format("DROP INDEX idx_pattern_realtime_time");
//            jdbcTemplate.execute(sql);
//            sql = String.format("create index IF NOT EXISTS idx_pattern_realtime_time on  pattern_realtime USING brin(time)");
//            jdbcTemplate.execute(sql);
//            logger.warning("Rebiuld Pattern Data Time Index Finished!");

            // 清理历史流量数据默
            sql = String.format("delete from flow where time < (now() - interval '%s day')",historyDataSaveTime);
            jdbcTemplate.execute(sql);
            logger.warning("Clear Flow Data From DB Finished!");


            // 清理历史操作记录默认
            sql = String.format("delete from operation_record where opertime < (now() - interval '%s day')",historyDataSaveTime);
            jdbcTemplate.execute(sql);
            logger.warning("Clear Operation Record From DB Finished!");

            // 清理历史相位、路口统计数据
            sql = String.format("delete from t_statistics where time < (now() - interval '%s day')",historyDataSaveTime);
            jdbcTemplate.execute(sql);
            logger.warning("Clear Statistics data From DB Finished!");

            // 重置路口数据源状态
            sql = "update dev set datavalid=null";
            jdbcTemplate.execute(sql);
            logger.warning("Rest Estimate Data Valid ");

            // 统计昨日设备在线数
            List<AscsBaseModel> ascsBaseModels = ascDao.getAscs();
            int online = 0;
            int offline = 0;
            for(AscsBaseModel ascsBaseModel : ascsBaseModels){
                if(ascsBaseModel.getState().equals("UP"))
                    online++;
                else if(ascsBaseModel.getState().equals("DOWN"))
                    offline++;

            }
            ascDao.statesCollectYesterday.setOnline(online);
            ascDao.statesCollectYesterday.setOffline(offline);

            // 统计设备故障数
            int fault = faultDao.getCurrentFaultDevCount();
            ascDao.statesCollectYesterday.setFault(fault);

            // 重置今日收到消息数
            MdTodayCount = 0;

            // 清空离线服务信息
            Set<String> keyset =  redisTemplate.getKeys("server:status:*");
            for(String key : keyset){
                String value = redisTemplate.getValue(key);
                if(value == null)
                    redisTemplate.delete(key);
                else {
                    ServerState serverState = gson.fromJson(value,ServerState.class);
                    if( compareCreateTime(serverState.getUpdatetime(), "yyyy-MM-dd HH:mm:ss", 300*1000) ){
                        redisTemplate.delete(key);
                    }
                }
            }
            logger.warning("Clearing Schedule Finished!");
        }catch (Exception e){
            logger.warning("clearHistoryData Error! " + e.getMessage());
        }
    }
}
